package model;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Random;
import java.util.concurrent.atomic.AtomicIntegerArray;

public class Cobra implements Runnable {

	// 0-3 para down,up,left,right
	private final int destino;
	final int cobra_id;
	public final Lock lock3 = new ReentrantLock(true);
	private AtomicIntegerArray validate;
	private volatile boolean has_turbo = false;

	public int getCobra_id() {
		return cobra_id;
	}

	private World w;
	private volatile int tamanho = 6;
	private volatile boolean has_food = false;

	public synchronized int getTamanho() {
		return tamanho;
	}

	public synchronized void setTamanho(int tamanho) {
		this.tamanho = tamanho;
	}

	private volatile int clock = 500;

	private boolean has_jump = false;

	public boolean getJumpcount() {
		return has_jump;
	}

	public void setJumpcount(boolean jumpcount) {
		this.has_jump = jumpcount;
	}

	@Override
	public void run() {
		colocaCobra();
		// !!!
		try {
			mexe();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void mexe() throws InterruptedException {

		// autopilot!
		while (w.celulas_cobras.containsKey(cobra_id)
				&& !w.destinos_cobras.containsKey(cobra_id)
				&& w.jogo_a_decorrer) {

			try {
				Thread.sleep(clock);

			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			movedir();

			// !!! modo manual de deslocação
			while ((w.celulas_cobras.containsKey(cobra_id)
					&& w.destinos_cobras.containsKey(cobra_id) && w.jogo_a_decorrer)) {
				try {
					Thread.sleep(clock);

				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				moveManual();

			}
		}

	}

	private synchronized void moveManual() throws InterruptedException {

		populaBonus();
		final AtomicIntegerArray validate2 = new AtomicIntegerArray(1);
		boolean valido = false;
		int moveu = -1;

		System.out.println(w.destinos_cobras.get(cobra_id));

		while (moveu == -1) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			// a alternar deslocacoes
			boolean pqsim = new Random().nextBoolean();

			if (w.destinos_cobras.get(cobra_id).get(1) > 0 && moveu == -1
					&& pqsim) {
				validate2.set(0, w.getDown(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 0;

			}

			if (w.destinos_cobras.get(cobra_id).get(1) < 0 && moveu == -1
					&& pqsim) {
				validate2.set(0, w.getUp(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 1;

			}
			if (w.destinos_cobras.get(cobra_id).get(0) > 0 && moveu == -1
					&& !pqsim) {
				validate2
						.set(0, w.getRight(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 2;

			}
			if (w.destinos_cobras.get(cobra_id).get(0) < 0 && moveu == -1
					&& !pqsim) {
				validate2.set(0, w.getLeft(w.getCelulasCobra(cobra_id).get(0)));
				moveu = 3;
			}
			// System.out.println("cobra " + cobra_id + " com destino a "
			// + validate2);
		}
		// if para cima

		// System.out.println("cobra " + cobra_id + " com destino a " +
		// validate2);

		if (has_turbo) {
			w.decCobraTurbo(cobra_id);

		}
		if (has_food) {

			if (valido = validaDestinoManual(has_jump, clock, validate2)) {
				++tamanho;
				validate = new AtomicIntegerArray(tamanho);
				// teste para ter turbo com comida.
				for (int i = 1; i < tamanho - 1 + validate2.length(); ++i) {
					validate.set(i, w.getCelulasCobra(cobra_id).get(i - 1));
				}

				validate.set(0, validate2.get(0));
				w.decreaseFood_counter(cobra_id);
				// System.out.println(validate);
			}
		} else {
			// System.out.println(valido);
			// if (valido = validaDestinos(has_jump, clock, validate2)) {
			if (valido = validaDestinoManual(has_jump, clock, validate2)) {
				validate = w.getCelulasCobra(cobra_id);
				for (int i = tamanho - 1; i > 0; --i) {
					validate.set(i, validate.get(i - 1));
				}
				validate.set(0, validate2.get(0));
				// System.out.println(validate);
			}
		}

		if (valido && w.jogo_a_decorrer == true) {
			aplicaBonus(validate);
			has_food = false;
			has_turbo = false;
			w.moveCobra(validate, cobra_id);
			eVencedora();
			switch (moveu) {
			case 0:
				w.destinos_cobras.get(cobra_id).decrementAndGet(1);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}

				break;

			case 1:
				w.destinos_cobras.get(cobra_id).incrementAndGet(1);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}
				break;

			case 2:
				w.destinos_cobras.get(cobra_id).decrementAndGet(0);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}
				break;

			case 3:
				w.destinos_cobras.get(cobra_id).incrementAndGet(0);
				if (w.destinos_cobras.get(cobra_id).get(0) == 0
						&& w.destinos_cobras.get(cobra_id).get(1) == 0) {
					w.destinos_cobras.remove(cobra_id);
					System.out.println("Cobra " + cobra_id
							+ "voltou a deslocacao aleatoria");
				}
				break;
			}

		} else {
			w.destinos_cobras.remove(cobra_id);
			System.out.println(cobra_id + " com destino aleatorio.");
		}
	}

	protected boolean validaDestinoManual(boolean tem_salto, int cobra_id,
			AtomicIntegerArray validate3) throws InterruptedException {

		final AtomicIntegerArray validate2 = validate3;
		System.out.println(validate3);
		// !!! vector de validacao mal construido

		for (int iii = 0; iii <= this.w.max_cobras; ++iii) {
			if (w.celulas_cobras.containsKey(iii)) {
				// && !w.celulas_cobras.containsKey(cobra_id)) {
				// System.out.println(validate2);
				for (int ii = 0; ii != w.getCelulasCobra(iii).length(); ++ii) {
					for (int i = 0; i != validate2.length(); ++i) {

						if (w.getCelulasCobra(iii).get(ii) == validate2.get(i)) {

							if (iii == cobra_id) {
								// System.out.println("Cobra " + cobra_id+
								// " é suicida");
								return false;
							}

							if (!tem_salto && iii != cobra_id) {
								// martelado!!!!
								lock3.tryLock(5, TimeUnit.SECONDS);
								try {

									while (w.getCelulasCobra(iii).get(ii) == validate2
											.get(i)
											&& iii != cobra_id
											&& w.jogo_a_decorrer) {
										System.out
												.println("Cobra "
														+ this.cobra_id
														+ " a aguardar por libertacao da celula"
														+ validate2.get(i));
									}
								} finally {
									lock3.unlock();
								}
								// alterado o return para devolver o estado de
								// ocupação da celula
								return (w.getCelulasCobra(iii).get(ii) != validate2
										.get(i));
							} else {
								w.setCobras_com_salto(cobra_id, false);
								System.out.println("Cobra " + cobra_id
										+ " Saltou!!");
								return true;
							}

						}
					}
				}
			}

			for (int a = 0; a != validate2.length(); ++a) {
				if ((w.getCelula(validate2.get(a)).isBlock())) {
					// System.out.println(cobra_id
					// + " encontrou Celula com obstaculo");
					return false;

				}
			}

		}
		// System.out.println(validate + "Celulas ok");
		return true;

	}

	private synchronized void movedir() {

		populaBonus();
		AtomicIntegerArray validate2 = new AtomicIntegerArray(1);
		boolean valido = false;
		int direccao = new Random().nextInt(4);
		switch (direccao) {
		case 0:
			validate2.set(0, w.getDown(w.getCelulasCobra(cobra_id).get(0)));
			break;
		case 1:
			validate2.set(0, w.getUp(w.getCelulasCobra(cobra_id).get(0)));
			break;

		case 2:
			validate2.set(0, w.getLeft(w.getCelulasCobra(cobra_id).get(0)));
			break;

		case 3:
			validate2.set(0, w.getRight(w.getCelulasCobra(cobra_id).get(0)));
			break;
		}

		if (has_turbo) {

			w.decCobraTurbo(cobra_id);
			validate2 = new AtomicIntegerArray(1);

			for (int i = 0; i < 3; ++i) {
				switch (new Random().nextInt(4)) {
				case 0:
					validate2.set(i,
							w.getDown(w.getCelulasCobra(cobra_id).get(0)));
					break;
				case 1:
					validate2.set(i,
							w.getUp(w.getCelulasCobra(cobra_id).get(0)));
					break;

				case 2:
					validate2.set(i,
							w.getLeft(w.getCelulasCobra(cobra_id).get(0)));
					break;

				case 3:
					validate2.set(i,
							w.getRight(w.getCelulasCobra(cobra_id).get(0)));
					break;
				}
			}

		}
		if (has_food & has_turbo) {
			// System.out.println(validate2);
			if (valido = validaDestino(has_jump, clock, validate2)) {

				++tamanho;
				validate = new AtomicIntegerArray(tamanho);
				// popula vector final a partir do id 3
				for (int i = 3; i < tamanho; ++i) {
					validate.set(i, w.getCelulasCobra(cobra_id).get(i - 3));
				}
				validate.set(0, validate2.get(0));
				w.decreaseFood_counter(cobra_id);
				has_food = false;
			}

			if (has_food) {
				// System.out.println(validate2);
				if (valido = validaDestino(has_jump, clock, validate2)) {
					// constroi o vector de valor, se definitivo e
					// valido
					++tamanho;
					validate = new AtomicIntegerArray(tamanho);

					for (int i = 1; i < tamanho; ++i) {
						validate.set(i, w.getCelulasCobra(cobra_id).get(i - 1));
					}
					validate.set(0, validate2.get(0));
					w.decreaseFood_counter(cobra_id);
					has_food = false;
				}

				if (!has_food & !has_turbo) {
					// System.out.println(valido);
					if (valido = validaDestino(has_jump, clock, validate2)) {
						validate = w.getCelulasCobra(cobra_id);
						for (int i = tamanho - 1; i > 0; --i) {
							validate.set(i, validate.get(i - 1));
						}
						validate.set(0, validate2.get(0));
					}
				}
			}

			if (valido) {
				aplicaBonus(validate);
				w.moveCobra(validate, cobra_id);
				eVencedora();
				// System.out.println(cobra_id + "tenteu mexer-se para "
				// + validate);
			} else {
				// System.out.println(cobra_id + " à espera de novo destino.");
			}
		}
	}

	private synchronized void populaBonus() {
		if (has_jump = w.get_salto_da_cobra(cobra_id))
			;
		if (w.getFood_counter(cobra_id) > 0) {
			has_food = true;
			w.decreaseFood_counter(cobra_id);
		}
		if (has_turbo = w.getCobraTurbo(cobra_id) > 0)
			;

	}

	synchronized boolean haTurbo(AtomicIntegerArray newcells) {

		boolean tem_turbo = false;
		for (int a = 0; a != newcells.length(); ++a) {
			if (w.getCelula(newcells.get(a)).isTurbo())
				tem_turbo = true;
		}
		return tem_turbo;
	}

	synchronized boolean haSalto(AtomicIntegerArray newcells) {

		boolean tem_salto = false;
		for (int a = 0; a != newcells.length(); ++a) {
			if (w.getCelula(newcells.get(a)).isJump())
				tem_salto = true;
		}
		return tem_salto;
	}

	private synchronized void colocaCobra() {

		boolean valido = false;
		resetValidate(destino);
		int counter = 0;

		switch (destino) {
		case 0:
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() * 5 + counter);
				validate.set(1, w.getLado() * 4 + counter);
				validate.set(2, w.getLado() * 3 + counter);
				validate.set(3, w.getLado() * 2 + counter);
				validate.set(4, w.getLado() + counter);
				validate.set(5, counter);
				++counter;

				if (validaDestino(has_jump, clock, validate)) {
					valido = true;
					break;
				}
			}
			break;

		case 1:
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() * w.getLado() - w.getLado() * 6
						+ counter);
				validate.set(1, w.getLado() * w.getLado() - w.getLado() * 5
						+ counter);
				validate.set(2, w.getLado() * w.getLado() - w.getLado() * 4
						+ counter);
				validate.set(3, w.getLado() * w.getLado() - w.getLado() * 3
						+ counter);
				validate.set(4, w.getLado() * w.getLado() - w.getLado() * 2
						+ counter);
				validate.set(5, w.getLado() * w.getLado() - w.getLado()
						+ counter);
				++counter;
				if (validaDestino(has_jump, clock, validate)) {
					valido = true;
					break;
				}
			}
			break;
		case 2:
			// for (int i = 0; i < w.getLado() & valido == false; ++i) {
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() - 6 + counter * w.getLado());
				validate.set(1, w.getLado() - 5 + counter * w.getLado());
				validate.set(2, w.getLado() - 4 + counter * w.getLado());
				validate.set(3, w.getLado() - 3 + counter * w.getLado());
				validate.set(4, w.getLado() - 2 + counter * w.getLado());
				validate.set(5, w.getLado() - 1 + counter * w.getLado());
				++counter;
				if (validaDestino(has_jump, clock, validate)) {
					valido = true;
					break;
				}
				;
			}
			break;
		case 3:
			while (counter < w.getLado() & valido == false) {
				validate.set(0, w.getLado() * counter + 5);
				validate.set(1, w.getLado() * counter + 4);
				validate.set(2, w.getLado() * counter + 3);
				validate.set(3, w.getLado() * counter + 2);
				validate.set(4, w.getLado() * counter + 1);
				validate.set(5, w.getLado() * counter);
				++counter;
				if (validaDestino(has_jump, clock, validate)) {
					valido = true;
					break;
				}
				;
			}
			break;
		}

		if (valido) {
			w.poeCobra(validate, cobra_id);
			w.cobras_com_salto.put(cobra_id, false);
			w.setControlo_humano_cobras(cobra_id, false);
			w.food_counter.put(cobra_id, 0);
			w.turbo_cobras.put(cobra_id, 0);
			has_turbo = false;
			has_food = false;
			/*
			 * w.setCobras_com_salto(cobra_id, false);
			 * w.turbo_cobras.set(cobra_id, 0); w.food_counter.set(cobra_id, 0);
			 * w.setControlo_humano_cobras(cobra_id, false);
			 * 
			 * w.controlo_humano_cobras.put(cobra_id, false);
			 */

		} else {
			// System.out.println("Não Colocada cobra em " + validate);
		}

		counter = 0;
		valido = false;

	}

	// metodo que faz reset a matriz de validação para as coordenandas de
	// partida de cada cobra
	private synchronized void resetValidate(int dir) {
		lock3.lock();
		try {
			switch (dir) {
			case 0:
				validate.set(0, w.getLado() * 5);
				validate.set(1, w.getLado() * 4);
				validate.set(2, w.getLado() * 3);
				validate.set(3, w.getLado() * 2);
				validate.set(4, w.getLado());
				validate.set(5, 0);
				break;
			case 1:
				validate.set(0, w.getLado() * w.getLado() - w.getLado() * 5);
				validate.set(1, w.getLado() * w.getLado() - w.getLado() * 4);
				validate.set(2, w.getLado() * w.getLado() - w.getLado() * 3);
				validate.set(3, w.getLado() * w.getLado() - w.getLado() * 2);
				validate.set(4, w.getLado() * w.getLado() - w.getLado() * 1);
				validate.set(5, w.getLado() * w.getLado() - w.getLado());
				break;
			case 2:
				validate.set(0, w.getLado() - 5 * w.getLado());
				validate.set(1, w.getLado() - 4 * w.getLado());
				validate.set(2, w.getLado() - 3 * w.getLado());
				validate.set(3, w.getLado() - 2 * w.getLado());
				validate.set(4, w.getLado() - 1 * w.getLado());
				validate.set(5, w.getLado() - 0 * w.getLado());
				break;
			case 3:
				validate.set(0, w.getLado() + 5);
				validate.set(1, w.getLado() + 4);
				validate.set(2, w.getLado() + 3);
				validate.set(3, w.getLado() + 2);
				validate.set(4, w.getLado() + 1);
				validate.set(5, w.getLado());
				break;
			}
			;
		} finally {
			lock3.unlock();
		}
	}

	private synchronized boolean validaDestino(boolean tem_salto, int clock,
			AtomicIntegerArray validate3) {

		final AtomicIntegerArray validate2 = validate3;
		// System.out.println(validate3);

		for (int iii = 0; iii <= w.max_cobras; ++iii) {
			if (w.celulas_cobras.containsKey(iii)) {
				// && !w.celulas_cobras.containsKey(cobra_id)) {
				// System.out.println(validate2);
				for (int ii = 0; ii != w.getCelulasCobra(iii).length(); ++ii) {
					for (int i = 0; i != validate2.length(); ++i) {

						if (w.getCelulasCobra(iii).get(ii) == validate2.get(i)) {

							if (iii == this.cobra_id) {
								// System.out.println("Cobra " + cobra_id+
								// " é suicida");
								return false;
							}

							if (!tem_salto) {
								// System.out.println(cobra_id
								// + "Tentou ir para cima da cobra"

								return false;
							} else {
								w.setCobras_com_salto(cobra_id, false);
								has_jump = false;
								w.setCobras_com_salto(cobra_id, false);
								// System.out.println("Cobra " + cobra_id
								// + " Saltou!!");
								return true;
							}

						}
					}
				}
			}

			for (int a = 0; a != validate2.length(); ++a) {
				if ((w.getCelula(validate2.get(a)).isBlock())
						&& w.jogo_a_decorrer) {
					// System.out.println(cobra_id
					// + " encontrou Celula com obstaculo");
					return false;

				}
			}

		}
		// System.out.println(validate + "Celulas ok");
		return true;

	}

	protected synchronized boolean eVencedora() {
		boolean e_vencedora = false;

		switch (destino) {
		case 0:
			if ((w.celulas_cobras.get(cobra_id).get(0) + w.lado) / w.lado == w.lado) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Ultima Fila atingida.");
			}
			break;
		case 1:
			if (w.celulas_cobras.get(cobra_id).get(0) / w.lado == 0) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Primeira Fila atingida.");
			}
			break;
		case 3:
			if ((w.celulas_cobras.get(cobra_id).get(0) + 1) % w.lado == 0) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Fila da direita atingida.");
			}
			break;
		case 2:
			if ((w.celulas_cobras.get(cobra_id).get(0)) % w.lado == 0) {
				e_vencedora = true;
				System.out
						.println("Cobra "
								+ cobra_id
								+ " venceu! A terminar a thread. Fila da esquerda atingida.");
			}
			break;
		}
		if (e_vencedora) {
			w.jogo_a_decorrer = false;
			System.out.println("Cobra " + cobra_id
					+ " venceu! A terminar a thread. Destino " + destino
					+ " atingido.");
		}
		return e_vencedora;
	}

	void aplicaBonus(AtomicIntegerArray Celulas) {

		if (quantaComida(Celulas) > 0)
			w.increaseFood_counter(cobra_id);

		if (haTurbo(Celulas))
			w.setCobraTurbo(cobra_id);

		if (haSalto(Celulas)) {
			w.setCobras_com_salto(cobra_id, true);
		}

	}

	public synchronized int quantaComida(AtomicIntegerArray newcells) {

		int temcomida = 0;
		for (int a = 0; a != newcells.length(); ++a) {
			if (w.getCelula(newcells.get(a)).isFood())
				++temcomida;
		}
		return temcomida;
	}

	// construtor para threads de acordo com o enunciado
	public Cobra(World w, int i) {
		this.w = w;
		cobra_id = i;
		destino = new Random().nextInt(4);//
		validate = new AtomicIntegerArray(tamanho);
		// System.out.println(cobra_id);

	}
}
